package sk.uniba.fmph.asdf.tempt;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.regex.Pattern;

import sk.uniba.fmph.asdf.SerializableEntity;
import sk.uniba.fmph.asdf.db.Account;

/**
 * Trieda predstavuje sablonu testu.
 *
 * @author Matej
 * @author tim
 * @author ivor
 * @serial
 */
public class TestTemplate extends SerializableEntity {

    private static final long serialVersionUID = 2012000001234L;

    // TODO: dohodnut sa na minimalnej dlzke mena sablony
    private static final Pattern validName = Pattern.compile("\\a{4,}");

    /**
     * @serialField
     */
	private int id;

    /**
     * @serialField
     */
	private String name;

    /**
     * @serialField
     */
	private Account authorAccount;

    /**
     * @serialField
     */
	private ArrayList<Account> evaluatorAccounts = new ArrayList<>();

    /**
     * @serialField
     */
	private ArrayList<PrimaryTestFieldDef> fieldDefs = new ArrayList<>();

	/**
	 * @serialField
	 */
	private long creationTimestamp;


    @Override
    public void validateState() {
        validateId(id);
        validateName(name);
        authorAccount.validateState();
        validateEvaluatorAccounts(evaluatorAccounts);
        validateFieldDefs(fieldDefs);
    }

    /**
     * Metoda overi spravnost mena sablony.
     * @param name meno sablony
     */
    private void validateName(final String name) {
        if (!validName.matcher(name).matches()) {
            throw new IllegalArgumentException(name);
        }
    }

    /**
     * Metoda overi spravnost uctov hodnotitelov.
     * @param evaluatorAccounts ucty hodnotitelov
     */
    private void validateEvaluatorAccounts(final ArrayList<Account> evaluatorAccounts) {
        for (Account account : evaluatorAccounts) {
            account.validateState();
        }
    }

    /**
     * Metoda overi spravnost definicii oblasti.
     * @param fieldDefs definicie oblasti
     */
    private void validateFieldDefs(final ArrayList<PrimaryTestFieldDef> fieldDefs) {
        for (PrimaryTestFieldDef fieldDef : fieldDefs) {
            fieldDef.validateState();
        }
    }

	public TestTemplate(int id, String name, Account authorAccount) {
		this.id = id;
		this.name = name;
		this.authorAccount = authorAccount;
		this.creationTimestamp = new java.util.Date().getTime();
	}

	public TestTemplate(int id, String name, Account authorAccount,
			ArrayList<PrimaryTestFieldDef> fieldDefs) {
		this(id, name, authorAccount);
		this.fieldDefs = fieldDefs;
	}

	public TestTemplate(int id, String name, Account authorAccount,
			ArrayList<PrimaryTestFieldDef> fieldDefs,
			ArrayList<Account> evaluatorAccounts) {
		this(id, name, authorAccount, fieldDefs);
		this.evaluatorAccounts = evaluatorAccounts;
	}

	public TestTemplate(String name, Account authorAccount, ArrayList<PrimaryTestFieldDef> fieldDefs) {
		this(-1, name, authorAccount, fieldDefs);
	}

	public TestTemplate(String name, Account authorAccount) {
		this(-1, name, authorAccount);
	}

	/**
	 * Prida hodnotitela do zoznamu hodnotitelov, ktori maju povolenie hodnotit
	 * testy s touto sablonou
	 *
	 * @param evaluator
	 *            ucet hodnotitela
	 * @return uspesnost pridania hodnotitela
	 */
	public boolean addEvaluator(Account evaluator) {
		if (!evaluatorAccounts.contains(evaluator)) {
			evaluatorAccounts.add(evaluator);
			return true;
		} else {
			return false;
		}
	}

	/**
	 * Odobranie prava hodnotit testy tejto sablony hodnotitelovi.
	 *
	 * @param evaluator
	 *            ucet odoberaneho hodnotitela
	 * @return uspesnost odobratia hodnotitela zo zoznamu
	 */
	public boolean deleteEvaluator(Account evaluator) {
		return evaluatorAccounts.remove(evaluator);
	}

	public String getName() {
		return name;
	}

	public Account getAuthorAccount() {
		return authorAccount;
	}

	public List<Account> getEvaluatorAccounts() {
		return evaluatorAccounts;
	}

	public List<PrimaryTestFieldDef> getFieldDefs() {
		return fieldDefs;
	}

    public int getId() {
        return id;
    }

    public void setId(int id) {
    	this.id = id;
    }

    public int getMaxPoints() {
    	int points = 0;
    	for (PrimaryTestFieldDef field : fieldDefs) {
    		points += field.getMaxPoints();
    	}
    	return points;
    }

    @Override
	public String toString() {
		return name;
	}

	@Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        } else if (o instanceof TestTemplate) {
            TestTemplate template = (TestTemplate) o;
            return template.name.equals(name);
        } else {
            return false;
        }
    }

    @Override
    public int hashCode() {
        int hash = 5;
        hash = 79 * hash + Objects.hashCode(this.name);
        return hash;
    }

    public static TestTemplate read(File file) throws IOException, ClassNotFoundException {
        try (FileInputStream fileIn = new FileInputStream(file); ObjectInputStream in = new ObjectInputStream(fileIn)) {
            TestTemplate template = (TestTemplate) in.readObject();
            in.close();
            fileIn.close();
            return template;
        }
    }

    public long getTimestamp() {
    	return creationTimestamp;
    }
}
